
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.Resource;

namespace Swaf.Call
{
	/// <summary>
	/// Summary description for BaseCall.
	/// </summary>
	public abstract class BaseCall : ICall
	{
		protected string m_id;
		protected CallParamOption m_paramHandling = CallParamOption.overridable;
		protected CallDistanceType m_callDistance = CallDistanceType.unknown;
		protected string m_className;
		protected string m_methodName;
		protected object[] m_baseParams;
		protected bool m_worksWithMultiCall = false;

		public BaseCall(string id)
		{
			m_id = id;
			m_methodName = m_id;
		}
		public BaseCall(IBizObj workerInfo)
		{
			m_id=workerInfo.get("id").ToString();
			m_className=(string) workerInfo.get("name");
			m_methodName=(string) workerInfo.get("methodName");
			m_baseParams= new object[] {};  // default to empty base params in case call has no params
			if (workerInfo.type.Fields.Contains("callDistance"))
			{
				string callDist = (string) workerInfo.get("callDistance");
				if (callDist != null && !callDist.Equals(string.Empty))
				{
					m_callDistance = (CallDistanceType)Enum.Parse(typeof(CallDistanceType), callDist, true);
				}
			}
		}

		public BaseCall(string id, XmlElement info)
			:this(id)
		{
			m_className = Application.globalApp.resMgr.resolve(info.GetAttribute("name"));
			m_methodName = Application.globalApp.resMgr.resolve(info.GetAttribute("method"));
			if(m_methodName == null || m_methodName.Length == 0)
				m_methodName = m_id;
			try
			{
				if(info.HasAttribute("paramHandling"))
					m_paramHandling = (CallParamOption)Enum.Parse(typeof(CallParamOption), info.GetAttribute("paramHandling"), true);
			}
			catch(Exception e)
			{
				throw new CallException(String.Format("Unknown paramHandling Option: {0}", info.GetAttribute("paramHandling")), e);
			}

			try
			{
				if(info.HasAttribute("multiCallSupport"))
					m_worksWithMultiCall = Boolean.Parse(info.GetAttribute("multiCallSupport"));
			}
			catch(Exception e)
			{
				throw new CallException(String.Format("Unknown multiCallSupport Option: {0}", info.GetAttribute("multiCallSupport")), e);
			}

			string cd = info.GetAttribute("callDistance");
			if(cd.Length != 0)
				m_callDistance = (CallDistanceType)Enum.Parse(typeof(CallDistanceType), cd, true);
			
			// Build the default parameters based on the <Params> defined in the config
			foreach(XmlNode n in info.ChildNodes)
			{
				if(n is XmlElement)
				{
					setupDefaultParams((XmlElement)n);
					break;
				}
			}
		}

		public BaseCall(string className, string methodName, params object[] p)
		{
			m_className = className;
			m_methodName = methodName;

			m_baseParams = p;
		}

		public string Id {get{return m_id;}}
		public bool WorksWithMultiCall {get{return m_worksWithMultiCall;}}

			public CallParamOption ParamHandling 
		{
			get{return m_paramHandling;}
			set{m_paramHandling = value;}
		}
		public string Method {get{return m_methodName;}}


		protected virtual object[] calcCallParams(object[] p)
		{
			//TODO: Add logic to use m_paramHandling option.
			if(m_paramHandling == CallParamOption.specifiedOnly || p == null || p.Length == 0)
				return m_baseParams;
			else
				return p;
		}

		public object[] CallParams
		{
			get{return m_baseParams;}
			set{m_baseParams = value;}
		}

		public virtual CallDistanceType CallDistance
		{
			get
			{
				return m_callDistance;
			}
		}

		protected void setupDefaultParams(XmlElement paramDef)
		{
			XmlNode paramsNode = paramDef.Name == "Params"?paramDef:paramDef.SelectSingleNode("Params");
			if (paramsNode != null)
			{
				XmlNodeList callParamNodes = paramsNode.SelectNodes("Param");
				m_baseParams = new object[callParamNodes.Count];
				for(int p = 0; p < m_baseParams.Length; ++p)
				{
					XmlElement param = (XmlElement) callParamNodes[p];
					string type = param.GetAttribute("type");
					object val = null;
					Type thisType = null;
					//If there is a child node for this param, pass it in as the value.
					if((type == null) || type.Equals(""))
						thisType = typeof(object);
					else
						thisType = Type.GetType(type);
					if (thisType == null)
						throw new BaseException(String.Format("Unable to create param type {0} from param definition {1}",type,param.OuterXml));

					string factoryMethod = param.GetAttribute("factoryMethod");
                    if (param.SelectSingleNode("NULL") != null)
                        m_baseParams[p] = null;
                    else
                    {
                        string tmpVal = param.InnerXml;
                        DotNetClassInfo info = new DotNetClassInfo(type, null, true, factoryMethod, null, false);
                        val = info.create(tmpVal);
                        //val=Serialization.deserialize(type,tmpVal);
                        m_baseParams[p] = val;
                    }
				}
			}
		}

		public abstract object call(params object[] p);
		public string className {get {return m_className;}}

	}
}
